我們今天要試著解決昨天閱讀 WhenAll 留下來的兩個問題
當 WhenAll 檢測到尚未完成的任務時會進入 task.AddCompletionAction(this);
這裡的 task 是待完成列表的其中一個任務, this 是運行 WhenAll 的本人。
跳過層層包裝我們會看到
private void AddCompletionAction(ITaskCompletionAction action, bool addBeforeOthers)
{
if (!AddTaskContinuation(action, addBeforeOthers))
action.Invoke(this);
}
當 AddTaskContinuation(action, addBeforeOthers)
為 false 表示任務已經完成, 在這裡調用 WhenAll 中的 Invoke 把待完成任務總數減1。
接著我們了解一下 AddCompletionAction
裡面發生甚麼事
private bool AddTaskContinuation(object tc, bool addBeforeOthers)
{
Contract.Requires(tc != null);
// 檢測目前所在執行列表裡的任務, 是否完成
if (IsCompleted) return false;
// 利用 CAS 把 tc ( WhenAllPromis 這個物件 ) 送入連續任務
if ((m_continuationObject != null) || (Interlocked.CompareExchange(ref m_continuationObject, tc, null) != null))
{
// 當一般 CAS 方法失敗後用這個一定成功的 CAS 方法。 不細講
return AddTaskContinuationComplex(tc, addBeforeOthers);
}
else return true; // 回傳 true, 成功把 WhenAllPromise 加入到這個執行列表裡的任務的連續任務區。
}
注 : WhenAllPromise 事當我們調用 WhenAll 所生成的物件, 上一篇文提到的事情都是在, WhenAllPromise 發生的
當 WhenAllPromise 這個物件被送入連續任務後發生了什麼呢 ?
這個疑問會在之後解答, 因為這個 method 就寫到這裡了。
當 Task.WhenAll( taskList )
調用後執行動作
taskList = 待完成任務列表, 我自己命名的變數
WhenAllPromise
物件, 且傳入 taskListWhenAllPromise
物件中WhenAllPromise
內部執行依序掃描 taskList 內的 taskWhenAllPromise
有可能被掛載到別的 task 執行, 就會被視為 multi-thread )WhenAllPromise
的 reference 掛載到 未完成的 task 的連續任務區(這個名詞是我自己取的 XD )WhenAllPromise
內的未完成任務數量為0, 表示任務全部完成, 當即設置回傳變數, 與進行回傳。WhenAllPromise
有在上面的第 5 步掛載到別的 task。WhenAllPromise
不會結束。至於被掛載進連續任務區的任務會被如何處理, 會在 Day 10 跟大家解釋清楚。值得注意的是因為每個 task 的連續任務區都有可能在任何時刻, 被別的 thread 添加, 因此也被設計成一個 lock-free data structure.
另外, 未完成任務數量因為WhenAllPromise
被掛載到別條 thread , 為了避免 race condition 也用了 atomic 變數。
開始看看 task 的創建, 試著了解 task 的生命歷程吧。
明天見!